import logging
import sys
from logging.handlers import SMTPHandler
import time


class CompatibleSMTPSSLHandler(SMTPHandler):
    """
    官方的SMTPHandler不支持SMTP_SSL的邮箱，这个可以两个都支持,并且支持邮件发送频率限制
    """

    def __init__(self, mailhost, fromaddr, toaddrs: tuple, subject,
                 credentials=None, secure=None, timeout=5.0, is_use_ssl=True, mail_time_interval=0):
        """

        :param mailhost:
        :param fromaddr:
        :param toaddrs:
        :param subject:
        :param credentials:
        :param secure:
        :param timeout:
        :param is_use_ssl:
        :param mail_time_interval: 发邮件的时间间隔，可以控制日志邮件的发送频率，为0不进行频率限制控制，如果为60，代表1分钟内最多发送一次邮件
        """
        super().__init__(mailhost, fromaddr, toaddrs, subject,
                         credentials, secure, timeout)
        self._is_use_ssl = is_use_ssl
        self._time_interval = mail_time_interval
        self._msg_map = dict()  # 是一个内容为键时间为值得映射

    def emit(self, record: logging.LogRecord):
        """
        Emit a record.

        Format the record and send it to the specified addressees.
        """
        from threading import Thread
        if sys.getsizeof(self._msg_map) > 10 * 1000 * 1000:
            self._msg_map.clear()
        Thread(target=self.__emit, args=(record,)).start()

    def __emit(self, record):
        if record.msg not in self._msg_map or time.time() - self._msg_map[record.msg] > self._time_interval:
            try:
                import smtplib
                from email.message import EmailMessage
                import email.utils
                t_start = time.time()
                port = self.mailport
                if not port:
                    port = smtplib.SMTP_PORT
                smtp = smtplib.SMTP_SSL(self.mailhost, port,
                                        timeout=self.timeout) if self._is_use_ssl else smtplib.SMTP(self.mailhost, port,
                                                                                                    timeout=self.timeout)
                msg = EmailMessage()
                msg['From'] = self.fromaddr
                msg['To'] = ','.join(self.toaddrs)
                msg['Subject'] = self.getSubject(record)
                msg['Date'] = email.utils.localtime()
                msg.set_content(self.format(record))
                if self.username:
                    if self.secure is not None:
                        smtp.ehlo()
                        smtp.starttls(*self.secure)
                        smtp.ehlo()
                    smtp.login(self.username, self.password)
                smtp.send_message(msg)
                smtp.quit()
                print('发送邮件给 {} 成功，用时 {}'.format(
                    self.toaddrs, round(time.time() - t_start, 2)))
                self._msg_map[record.msg] = time.time()
            except Exception:
                self.handleError(record)

        else:
            pass
            print('邮件发送太频繁，此次不发送这个邮件内容:{}'.format(record.msg))


def main():
    # 1. 创建 logger 实例
    logger = logging.getLogger('test-smtphandler')
    # 2. 设置 logger 实例的日志级别，默认是 logging.WARNING
    logger.setLevel(logging.INFO)
    # 3. 创建 Handler
    # 注意 163 邮箱要求 fromaddr 和你发送邮件的邮箱(即你的邮箱账号)要一致
    mail_handler = CompatibleSMTPSSLHandler(
        mailhost=('smtp.126.com', 465),
        fromaddr='abc@126.com',
        toaddrs=['abc@outlook.com', 'abc@163.com'],
        subject='服务器出现问题啦！！！',
        credentials=('abc@126.com', 'safsdfasdfas'))
    # 4. 单独设置 mail_handler 的日志级别为 ERROR
    mail_handler.setLevel(logging.ERROR)
    # 5. 将 Handler 添加到 logger 中
    logger.addHandler(mail_handler)

    # 6. 应用的业务代码(故意出错)
    try:
        x = 1 / 0
    except Exception:
        logger.error('[计算出错了] x = 1 / 0', exc_info=sys.exc_info())


if __name__ == '__main__':
    main()
